package com.wish.nettywebsocket;

import java.util.Date;

import com.wish.common.MyUtils;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.*;
import io.netty.handler.codec.http.websocketx.*;
import io.netty.util.CharsetUtil;


/**
 * @author tantexian
 * @since 2016/7/15
 */
public class WebSocketServerHandler extends SimpleChannelInboundHandler {

    private WebSocketServerHandshaker webSocketServerHandshaker;
    private String socketUrl = "ws://" + MyUtils.getDefaultServerAddr() + ":" + MyUtils.getDefaultPort() + "/websocket";


    WebSocketServerHandler(String socketUrl){
        this.socketUrl = socketUrl;
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ctx.flush();
    }


    protected void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
        // 如果为httprequest
        if (msg instanceof FullHttpRequest) {
            // 因为Websocket借助了http的upgrade，因此第一次如果发现是http且upgrade为Websocket，
            // 则将当前http升级为websocket，进行握手建立连接，后续请求则直接为WebSocketFrame
            handleHttpRequest(ctx, (FullHttpRequest) msg);
        }
        // 如果为Websocketrequest
        else if (msg instanceof WebSocketFrame) {
            handleWebSocket(ctx, (WebSocketFrame) msg);
        }

    }


    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }


    private void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest request) {
        // Http解码失败 或者 当前请求的headers使用Upgrede:websocke(即websocket协议，因为Websocket是基于http的升级)
        if (!request.getDecoderResult().isSuccess()
                || !("websocket".equals(request.headers().get("Upgrade")))) {
            // 返回BAD_REQUEST异常
            DefaultFullHttpResponse response =
                    new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST);
            // 返回应答给客户端
            if (response.getStatus().code() != 200) {
                ByteBuf byteBuf = Unpooled.copiedBuffer(response.getStatus().toString(), CharsetUtil.UTF_8);
                response.content().writeBytes(byteBuf);
                HttpHeaders.setContentLength(response, response.content().readableBytes());
            }
            // 如果是非keep-lived则关闭链接
            ChannelFuture channelFuture = ctx.channel().writeAndFlush(response);
            if (!HttpHeaders.isKeepAlive(request) || response.getStatus().code() != 200) {
                channelFuture.addListener(ChannelFutureListener.CLOSE);
            }
            return;
        }

        // 如果为Upgrede:websocke协议请求，则构造握手响应返回，本机测试
        WebSocketServerHandshakerFactory wsFactory =
                new WebSocketServerHandshakerFactory(socketUrl, null, false);
        webSocketServerHandshaker = wsFactory.newHandshaker(request);
        if (webSocketServerHandshaker == null) {
            WebSocketServerHandshakerFactory.sendUnsupportedWebSocketVersionResponse(ctx.channel());
        }
        else {
            webSocketServerHandshaker.handshake(ctx.channel(), request);
        }
    }


    private void handleWebSocket(ChannelHandlerContext ctx, WebSocketFrame webSocketFrame) {
        // 判断是否为关闭链路指令
        if (webSocketFrame instanceof CloseWebSocketFrame) {
            webSocketServerHandshaker.close(ctx.channel(), (CloseWebSocketFrame) webSocketFrame.retain());
            return;
        }

        // 如果是ping
        if (webSocketFrame instanceof PingWebSocketFrame) {
            ctx.channel().write(new PongWebSocketFrame(webSocketFrame.content().retain()));
            return;
        }

        if (!(webSocketFrame instanceof TextWebSocketFrame)) {
            throw new UnsupportedOperationException(
                String.format("%s frame types not supported", webSocketFrame.getClass().getName()));
        }

        // 返回应答消息
        String requestText = ((TextWebSocketFrame) webSocketFrame).text();

        ctx.channel().write(
            new TextWebSocketFrame(requestText + ", 欢迎使用netty websocket服务，当前时间为：" + new Date().toString()));
    }

}
